package com.jsmile.cloud.taskcenter.task.kit;

import java.lang.reflect.Method;
import java.text.ParseException;
import java.util.*;

import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;

import com.google.common.base.Preconditions;
import com.jsmile.cloud.taskcenter.api.constants.TaskConstants;
import com.jsmile.cloud.taskcenter.task.model.TaskModel;
import com.jsmile.cloud.taskcenter.task.quartz.QuartzJobFactory;
import com.jsmile.cloud.taskcenter.task.quartz.QuartzJobFactoryDisallowConcurrentExecution;

import cn.hutool.extra.spring.SpringUtil;

/**
 * @author 龚亮
 * @description 定时任务工具类
 * @date 2015-05-27 14:22:13
 */
public class TaskKit {

    private static final Logger logger = LoggerFactory.getLogger(TaskKit.class);

    private static SchedulerFactoryBean schedulerFactoryBean = SpringUtil.getBean(SchedulerFactoryBean.class);

    /**
     * 获取所有已经存在的job
     */
    public static List<TaskModel> getAllJob() throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        List<TaskModel> jobList = new ArrayList<TaskModel>();
        for (JobKey jobKey : jobKeys) {
            List<? extends Trigger> triggers = scheduler.getTriggersOfJob(jobKey);
            for (Trigger trigger : triggers) {
                TaskModel job = new TaskModel();
                job.setJobName(jobKey.getName());
                job.setJobGroup(jobKey.getGroup());
                Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
                job.setRemark("触发器:" + trigger.getKey() + ":状态" + triggerState.name());
                job.setTaskStatus(TaskConstants.TASK.STATUS_NORMAL);
                if (trigger instanceof CronTrigger) {
                    CronTrigger cronTrigger = (CronTrigger)trigger;
                    String cronExpression = cronTrigger.getCronExpression();
                    job.setCron(cronExpression);
                }
                jobList.add(job);
            }
        }
        return jobList;
    }

    /**
     * 判断某个job是否存在
     *
     * @param jobName
     *            任务名称
     * @param jobGroup
     *            任务组
     */
    public static boolean existsJob(String jobName, String jobGroup) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(jobName, jobGroup);
        return scheduler.checkExists(jobKey);
    }

    /**
     * 判断某个job是否存在,存在就修改cron,不存在新加
     */
    public static void existsJobUpdateCron(TaskModel task) throws SchedulerException, ParseException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(task.getJobName(), task.getJobGroup());
        Trigger trigger = scheduler.getTrigger(triggerKey);
        CronTrigger cronTrigger = (CronTrigger)trigger;
        if (null == cronTrigger) {
            if (CronKit.isCronPeriod(task.getCron())) {
                TaskKit.addJob(task, null);
            }
        } else {
            String cronExpression = cronTrigger.getCronExpression();
            if (!Objects.equals(task.getCron(), cronExpression)) {
                TaskKit.updateJobCron(task);
            }
        }
    }

    /**
     * 删除一个任务
     */
    public static void deleteJob(TaskModel task) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
        scheduler.deleteJob(jobKey);
    }

    /**
     * 暂停一个任务
     */
    public static void pauseJob(TaskModel task) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复一个任务
     */
    public static void resumeJob(TaskModel task) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
        scheduler.resumeJob(jobKey);
    }

    /**
     * 立即执行任务
     */
    public static void runAJobNow(TaskModel task) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        JobKey jobKey = JobKey.jobKey(task.getJobName(), task.getJobGroup());
        scheduler.triggerJob(jobKey);
    }

    /**
     * 更改任务cron表达式
     */
    public static void updateJobCron(TaskModel task) throws SchedulerException {
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(task.getJobName(), task.getJobGroup());
        CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCron()).withMisfireHandlingInstructionDoNothing();
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
        scheduler.rescheduleJob(triggerKey, trigger);
    }

    /**
     * 添加一个任务
     */
    @SuppressWarnings("unchecked")
    public static void addJob(TaskModel task, Map<String, Object> data) throws SchedulerException {
        Preconditions.checkNotNull(task.getJobName(), "任务名称不能为空!");
        Preconditions.checkNotNull(task.getJobGroup(), "任务所属组不能为空!");
        Preconditions.checkNotNull(task.getCron(), "cron表达式不能为空!");
        Scheduler scheduler = schedulerFactoryBean.getScheduler();
        if (task == null || TaskConstants.TASK.STATUS_STOP == task.getTaskStatus()) {
            logger.error("任务名称 = [" + task.getJobName() + ",任务=" + task.getBeanClass() + "." + "execute" + "]---------------添加失败，请检查是否配置正确！！！");
            return;
        }
        TriggerKey triggerKey = TriggerKey.triggerKey(task.getJobName(), task.getJobGroup());

        CronTrigger trigger = (CronTrigger)scheduler.getTrigger(triggerKey);

        // 不存在，创建一个
        if (null == trigger) {
            Class<?> clazz = TaskConstants.TASK.STATUS_NORMAL == task.getStatus() ? QuartzJobFactoryDisallowConcurrentExecution.class : QuartzJobFactory.class;
            JobDetail jobDetail = JobBuilder.newJob((Class<? extends Job>)clazz).withIdentity(task.getJobName(), task.getJobGroup()).build();
            jobDetail.getJobDataMap().put(TaskConstants.TASK.SCHEDULE_JOB, task);
            jobDetail.getJobDataMap().put(TaskConstants.TASK.JOB_DATA_MAP, data);
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCron());
            trigger = TriggerBuilder.newTrigger().withIdentity(task.getJobName(), task.getJobGroup()).withSchedule(scheduleBuilder).build();
            scheduler.scheduleJob(jobDetail, trigger);
        } else {
            // Trigger已存在，那么更新相应的定时设置
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(task.getCron());
            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            // 按新的trigger重新设置job执行
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    }

    /**
     * 通过反射调用scheduleJob中定义的方法
     */
    public static void invokMethod(TaskModel schedule, Object... args) {
        Object object = null;
        Class<?> clazz = null;
        if (null != schedule.getBeanClass()) {
            try {
                clazz = Class.forName(schedule.getBeanClass());
                object = clazz.newInstance();
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }

        }
        if (object == null) {
            logger.error("任务名称 = [" + schedule.getJobName() + ",任务=" + schedule.getBeanClass() + "." + "execute" + "]---------------未启动成功，请检查是否配置正确！！！");
            return;
        }
        clazz = object.getClass();
        Method method = null;
        try {
            method = clazz.getDeclaredMethod("execute", Map.class);
        } catch (NoSuchMethodException e) {
            logger.error("任务名称 = [" + schedule.getJobName() + ",任务=" + schedule.getBeanClass() + "." + "execute" + "]---------------未启动成功，方法名设置错误！！！");
        } catch (SecurityException e) {
            logger.error(e.getMessage(), e);
        }
        if (method != null) {
            try {
                method.invoke(object, args);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
    }
}
